package com.berchen.Buffer;

import org.junit.Test;

import java.nio.Buffer;
import java.nio.ByteBuffer;

/**
 * 缓冲区：在JAVA NIO中负责数据的存取。缓冲区底层就是数组，用于存储不同数据类型的数据。
 *  根据数据类型不同（boolean类型除外，其他7中都有），提供了相应类型的缓冲区。
 *  ByteBuffer（崔最常用）
 *  CharBuffer
 *  ShortBuffer
 *  IntBuffer
 *  LongBuffer
 *  FloatBuffer
 *  DoubleBuffer
 *
 *  上述缓冲区的管理方式几乎一致。通过allocate()方法获取缓冲区。
 *
 *  缓冲区存取数据的核心方法：
 *      put()：存数据到缓冲区中。
 *      get()：获取缓冲区中的数据.    当position移动到没有数据的地方的时候，get就会报错。
 *      flip()：切换到读数据模式。
 *      rewind()：从读完之后回到读数据模式。
 *      clear()：清空缓冲区，只是让缓冲区回到最开始的时候，数据一直存在。
 *      mark()：标记
 *      reset()：恢复到mark标记的位置。
 *      hasRemaining()：判断缓冲区中是否有数据
 *      remaining()：查看缓冲区中可以操作的数据的数量
 *      isDirect()：判断是否是直接缓冲区
 *
 *  Buffer中的核心属性：
 *  // Invariants:mark <= position <= limit <= capacity
 *     private int mark = -1;       // 标记，表示记录当前position的位置，可以通过reset() 恢复到mark的位置
 *     private int position = 0;    // 位置，表示缓冲区中正在操作 数据的位置。
 *     private int limit;           // 界限，表示缓冲区中的可以操作数据的大小（limit后面数据是不能进行读写的）。
 *     private int capacity;        // 容量，表示缓冲区中最大存储数据的容量。一旦声明之后该值就不能改变。
 *
 * 非直接缓冲区：allocate()方法分配缓冲区，将缓冲区建立在JVM的内存中。
 * 直接缓冲区：allocateDirect()方法分配缓冲区，将缓冲区建立在物理内存中。可以提高效率。消费的资源较大，并且不容易控制。
 *  为什么在物理内存中就可以提高效率？
 *      缓冲区建立在JVM中，从磁盘读取文件->read->物理内存中->【copy】->JVM的内存地址->read->应用程序中。
 *      缓冲区建立在物理内存中：磁盘读取文件->read->物理内存映射文件->read->应用程序中。
 *      两者相比，减少了copy过程。提高了效率。
 *
 *
 */
public class TestBuffer {

    @Test
    public void test3(){

        // 创建直接缓冲区
        ByteBuffer buf = ByteBuffer.allocateDirect(1024);

        // 判断是否是直接缓冲区
        System.out.println(buf.isDirect()); // true
    }

    @Test
    public void test2(){

        ByteBuffer buf = ByteBuffer.allocate(1024); // 非直接缓冲区
        buf.put("chen".getBytes());
        buf.flip();

        byte[] b=new byte[buf.limit()];
        buf.get(b,0,2);
        System.out.println(new String(b,0,2));
        soutBuffer(buf);        // 2 4 1024


        // 标记，调用一次该方法就会标记一次
        buf.mark();


        buf.get(b,2,2);
        System.out.println(new String(b,2,2));

        soutBuffer(buf);        // 4 4 1024

        // 恢复到mark的位置
        buf.reset();
        soutBuffer(buf);        // 2 4 1024

        if(buf.hasRemaining()){ // 判断缓冲区中是否有数据

            System.out.println(buf.remaining());    // 可操作的数据的数量
        }

    }

    @Test
    public void test1(){
        // 分配一个指定大小的缓冲区
        ByteBuffer buf = ByteBuffer.allocate(1024);

        // 打印Buffer的四个核心属性
        soutBuffer(buf);    //  0 1024 1024

        // 往缓冲区中存储数据
        buf.put("chen".getBytes());

        // 上面存储了4个数据
        soutBuffer(buf);    //  4 1024 1024

        // 切换读数据模式
        buf.flip();

        soutBuffer(buf);    //  0 4 1024

        // 读取缓冲区中的数据
        byte[] read =new byte[buf.limit()];
        buf.get(read);
        System.out.println(new String(read,0,read.length));
        soutBuffer(buf);    //  4 4 1024

        // 回到读模式，可以重读
        buf.rewind();
        soutBuffer(buf);    //  0 4 1024

        // 清空缓冲区
        buf.clear();
        soutBuffer(buf);    //  0 1024 1024

    }

    public void soutBuffer(Buffer buf){

        System.out.println(buf.position());
        System.out.println(buf.limit());
        System.out.println(buf.capacity());
    }
}
